package com.ideaaedi.hot.clazz.update;

import com.ideaaedi.hot.clazz.update.exception.HotUpdateClassException;
import com.ideaaedi.hot.clazz.update.memory.MemoryTransferMap;
import com.ideaaedi.hot.common.HotUpdateExecutor;
import com.ideaaedi.hot.common.HotUpdateType;
import sun.instrument.InstrumentationImpl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

/**
 * 对{@link ClassHotUpdateInnerExecutor}进行简单包装，使{@link ClassLoader#getSystemClassLoader()}加载相关的类。
 * <br>即：使其类加载器与执行{@link InstrumentationImpl#loadClassAndCallAgentmain(java.lang.String, java.lang.String)}方
 * 法时的类加载器一致，避免类隔离，进而使得{@link MemoryTransferMap#globalMemory()}全局内存有效。
 *
 * @author JustryDeng
 * @since 1.0.0
 */
public class ClassHotUpdateExecutor {
    
    /** 添加资源的方法 */
    private static final Method ADD_URL_METHOD;
    
    /** 类加载器 */
    private static final URLClassLoader CLASS_LOADER;
    
    private static final Method base64ClassPathsExecMethodOne;
    private static final Method base64ClassPathsExecMethodTwo;
    private static final Method base64ClassPathsExecMethodThree;
    
    private static final Method base64JavaPathsExecMethodOne;
    private static final Method base64JavaPathsExecMethodTwo;
    private static final Method base64JavaPathsExecMethodThree;
    
    private static final Method memoryClassBytesExecMethodOne;
    private static final Method memoryClassBytesExecMethodTwo;
    
    private static final Method memoryJavaBytesExecMethodOne;
    private static final Method memoryJavaBytesExecMethodTwo;
    
    static {
        try {
            ADD_URL_METHOD = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            ADD_URL_METHOD.setAccessible(true);
            CLASS_LOADER = (URLClassLoader) ClassLoader.getSystemClassLoader();
            for (URL url : ((URLClassLoader) ClassHotUpdateInnerExecutor.class.getClassLoader()).getURLs()) {
                ADD_URL_METHOD.invoke(CLASS_LOADER, url);
            }
    
            Class<?> base64ClassPathsClass = CLASS_LOADER.loadClass("com.ideaaedi.hot.clazz.update"
                    + ".ClassHotUpdateInnerExecutor$BASE64_CLASS_PATHS");
            base64ClassPathsExecMethodOne = base64ClassPathsClass.getMethod("execute", List.class);
            base64ClassPathsExecMethodTwo = base64ClassPathsClass.getMethod("execute", String.class, List.class);
            base64ClassPathsExecMethodThree = base64ClassPathsClass.getMethod("execute", String.class, String.class, List.class);
            
            Class<?> base64JavaPathsClass = CLASS_LOADER.loadClass("com.ideaaedi.hot.clazz.update.ClassHotUpdateInnerExecutor$BASE64_JAVA_PATHS");
            base64JavaPathsExecMethodOne = base64JavaPathsClass.getMethod("execute", List.class);
            base64JavaPathsExecMethodTwo = base64JavaPathsClass.getMethod("execute", String.class, List.class);
            base64JavaPathsExecMethodThree = base64JavaPathsClass.getMethod("execute", String.class, String.class, List.class);
            
            Class<?> memoryClassBytesClass = CLASS_LOADER.loadClass(
                    "com.ideaaedi.hot.clazz.update.ClassHotUpdateInnerExecutor$MEMORY_CLASS_BYTES");
            memoryClassBytesExecMethodOne = memoryClassBytesClass.getMethod("execute", List.class);
            memoryClassBytesExecMethodTwo = memoryClassBytesClass.getMethod("execute", String.class, List.class);
            
            Class<?> memoryJavaBytesClass = CLASS_LOADER.loadClass("com.ideaaedi.hot.clazz.update.ClassHotUpdateInnerExecutor$MEMORY_JAVA_BYTES");
            memoryJavaBytesExecMethodOne = memoryJavaBytesClass.getMethod("execute", List.class);
            memoryJavaBytesExecMethodTwo = memoryJavaBytesClass.getMethod("execute", String.class, List.class);
        } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
            throw new HotUpdateClassException(e);
        }
    }
    
    /**
     * 基于class字节码，走内存进行class热更新
     */
    public static class MEMORY_CLASS_BYTES implements HotUpdateExecutor {
        
        /**
         * @see ClassHotUpdateExecutor.MEMORY_CLASS_BYTES#execute(String, List)
         */
        public static void execute(List<byte[]> classBytesList) {
            try {
                memoryClassBytesExecMethodOne.invoke(null, classBytesList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
        
        /**
         * 基于class字节码，走内存进行class热更新
         *
         * @param agentJarPath
         *            代理jar包的绝对路径
         * @param classBytesList
         *            要进行更新的class字节码文件的绝对路径集合
         */
        public static void execute(String agentJarPath, List<byte[]> classBytesList) {
            try {
                memoryClassBytesExecMethodTwo.invoke(null, agentJarPath, classBytesList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
    
        @Override
        public HotUpdateType type() {
            return HotUpdateType.CLASS;
        }
    }
    
    /**
     * 基于java源码，走内存进行class热更新
     */
    public static class MEMORY_JAVA_BYTES implements HotUpdateExecutor {
        
        /**
         * @see ClassHotUpdateExecutor.MEMORY_JAVA_BYTES#execute(String, List)
         */
        public static void execute(List<byte[]> javaBytesList) {
            try {
                memoryJavaBytesExecMethodOne.invoke(null, javaBytesList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
        
        /**
         * 基于java源码，走内存进行class热更新
         *
         * @param agentJarPath
         *            代理jar包的绝对路径
         * @param javaBytesList
         *            要进行更新的java源码文件的绝对路径集合
         */
        public static void execute(String agentJarPath, List<byte[]> javaBytesList) {
            try {
                memoryJavaBytesExecMethodTwo.invoke(null, agentJarPath, javaBytesList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
    
        @Override
        public HotUpdateType type() {
            return HotUpdateType.CLASS;
        }
    }
    
    /**
     * 基于class字节码文件的class热更新
     */
    public static class BASE64_CLASS_PATHS implements HotUpdateExecutor {

        /**
         * @see ClassHotUpdateExecutor.BASE64_CLASS_PATHS#execute(String, List)
         */
        public static void execute(List<String> classPathsList) {
            try {
                base64ClassPathsExecMethodOne.invoke(null, classPathsList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
    
        /**
         * @see ClassHotUpdateExecutor.BASE64_CLASS_PATHS#execute(String, String, List)
         */
        public static void execute(String agentJarPath, List<String> classPathsList) {
            try {
                base64ClassPathsExecMethodTwo.invoke(null, agentJarPath, classPathsList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
        
        /**
         * 基于class字节码文件的class热更新
         *
         * @param pid
         *            要热更新的jvm的进程id
         * @param agentJarPath
         *            代理jar包的绝对路径
         * @param classPathsList
         *            要进行更新的class字节码文件的绝对路径集合
         */
        public static void execute(String pid, String agentJarPath, List<String> classPathsList) {
            try {
                base64ClassPathsExecMethodThree.invoke(null, pid, agentJarPath, classPathsList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
    
        @Override
        public HotUpdateType type() {
            return HotUpdateType.CLASS;
        }
    }
    
    /**
     * 基于java源码文件的class热更新
     */
    public static class BASE64_JAVA_PATHS implements HotUpdateExecutor {
        
        /**
         * @see ClassHotUpdateExecutor.BASE64_JAVA_PATHS#execute(String, List)
         */
        public static void execute(List<String> javaPathsList) {
            try {
                base64JavaPathsExecMethodOne.invoke(null, javaPathsList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
        
        /**
         * @see ClassHotUpdateExecutor.BASE64_JAVA_PATHS#execute(String, String, List)
         */
        public static void execute(String agentJarPath, List<String> javaPathsList) {
            try {
                base64JavaPathsExecMethodTwo.invoke(null, agentJarPath, javaPathsList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
        
        /**
         * 基于java源码文件的class热更新
         *
         * @param pid
         *            要热更新的jvm的进程id
         * @param agentJarPath
         *            代理jar包的绝对路径
         * @param javaPathsList
         *            要进行更新的java类文件的绝对路径集合
         */
        public static void execute(String pid, String agentJarPath, List<String> javaPathsList) {
            try {
                base64JavaPathsExecMethodThree.invoke(null, pid, agentJarPath, javaPathsList);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new HotUpdateClassException(e);
            }
        }
    
        @Override
        public HotUpdateType type() {
            return HotUpdateType.CLASS;
        }
    }
}
